package natappvip.liujinliang.db;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import natappvip.liujinliang.exception.JdbcException;
import natappvip.liujinliang.util.PropertiesUtil;

/**
 * @ClassName:  SimpleConnectionPoolDataSource   
 * @Despt: 线程池，目前只考虑最大最小连接数
 * @Auth: Liujinliang 
 * @Date: 2018年7月25日 下午10:03:19
 */
public class DbConnectionPool {
	
	private static Logger logger = LoggerFactory.getLogger(DbConnectionPool.class);

	private static String username;
	private static String password;
	private static String url;
	private static int increase;		// 连接增量（每次只要创建两个连接哦）
	private static int minConns;		// 最小连接数
	private static int maxConns;		// 最大连接数
	private static int isUsed = 0;		// 正在使用的连接数
	private static int interval;		// 等待间隔
	private static List<Connection> conns;		// 维护可重用的 Connection 对象
	
	public DbConnectionPool() {
		this("datasource.properties");
	}
	
	public DbConnectionPool(String configFileName) {
		
//		username = bundle.getString("jdbc.oracle.fs.username");
//		password = bundle.getString("jdbc.oracle.fs.password");
//		url = bundle.getString("jdbc.oracle.fs.url");
		
		username = PropertiesUtil.getProperty("jdbc.mysql.fs.username");
		password = PropertiesUtil.getProperty("jdbc.mysql.fs.password");
		url = PropertiesUtil.getProperty("jdbc.mysql.fs.url");
		increase = Integer.parseInt(PropertiesUtil.getProperty("jdbc.fs.increase"));
		minConns = Integer.parseInt(PropertiesUtil.getProperty("jdbc.fs.min"));
		maxConns = Integer.parseInt(PropertiesUtil.getProperty("jdbc.fs.max"));
		interval = Integer.parseInt(PropertiesUtil.getProperty("jdbc.fs.interval"));
		
		conns = Collections.synchronizedList(new ArrayList<Connection>());
	}
	
	/**
	 * @Title: createNewConnection   
	 * @Despt: 创建新连接，修正连接数   
	 * @param: @param inc      
	 * @return: void      
	 * @throws
	 */
	public void createNewConnection(int inc) {
		
		// 每次创建 inc 个连接
		for (int x = 0; x < inc; x++) {
			
			// 如果当前连接数已经达到最大，就直接退出吧
			if (conns.size() + isUsed >= maxConns) {
				logger.info("当前连接数{}已经大于最大连接数{}", conns.size() + isUsed, maxConns);
				break;
			}
			
			// 如果当前连接数没有达到最大，就创建一个新的连接并放入到连接池
			Connection conn = null;
			try {
				conn = DriverManager.getConnection(url, username, password);
			} catch (SQLException e) {
				logger.info("连接创建失败，校验url:{},校验username:{},校验password{}", url, username, password);
				throw new JdbcException(e.getMessage());
			}
			
			// 如果创建的是第一个连接，需要使用这个连接获取数据库MetaData，来重新修订我们之前的连接数目
			if (conn != null && conns.size() == 0 && isUsed == 0) {
				DatabaseMetaData databaseMetaData = null;
				try {
					// 获取 metaData
					databaseMetaData = conn.getMetaData();
					
					// 返回数据库允许的最大连接数目
					int maxConnectionAllowdInDb = databaseMetaData.getMaxConnections();
					
					// 修正最小连接数目
					if ((maxConnectionAllowdInDb > 0) && (minConns > maxConnectionAllowdInDb))
						minConns = 1;
					
					// 修正最大连接数目
					if ((maxConnectionAllowdInDb > 0) && (maxConns > maxConnectionAllowdInDb))
						maxConns = maxConnectionAllowdInDb;
					
				} catch (SQLException e) {
					logger.info("重新修订连接数目失败");
					throw new JdbcException(e.getMessage());
				}
			}
			
			logger.info("新连接创建成功---" + conn);
			conns.add(conn);
		}
	}
	
	/**
	 * @Title: freeConnection   
	 * @Despt: 释放连接到连接池   
	 * @param: @param conn      
	 * @return: void      
	 * @throws
	 */
	public void freeConnection(Connection conn) {
		try {
			if (conn != null && (! conn.isClosed())) {
				// 提交
				conn.setAutoCommit(true);
				
				// 放入连接池
				conns.add(conn);
				
				if (isUsed > 0) 
					isUsed--;
				
				logger.info("free num:{}, used num:{}", conns.size(), isUsed);
			}
		} catch (SQLException e) {
			logger.error("释放连接出现异常");
			throw new JdbcException(e.getMessage());
		}
	}
	
	/**
	 * @Title: getConnection   
	 * @Despt: 递归函数，递归调用自身，获取连接 
	 * @param: @return      
	 * @return: Connection      
	 * @throws
	 */
	public Connection getConnection() {
		Connection conn = null;
		
		if (conns.size() > 0) {		// 如果连接池有可用连接 	
			// 返回并且从连接池中移除该连接
			conn = conns.remove(0);
			
			try {
				if (conn.isClosed() || conn == null) {
					// 递归调用函数，尝试再次取得连接
					conn = getConnection();
				}
			} catch (SQLException e) {
				logger.info("connection error:{}, 提示信息:{}", conn, e.getMessage());
				// 递归调用函数，尝试再次取得连接
				conn = getConnection();
			}
			isUsed++;
			logger.info("free num:{}, busy num:{}", conns.size(), isUsed);
			return conn;
		}
		else {		// 如果连接池无可用连接
			if (isUsed + increase <= maxConns) {	// 如果当前正在使用的连接数小于最大连接数
				createNewConnection(increase);
				
				// 递归调用函数，尝试再次取得连接
				return getConnection();
			}
			else { 	// 如果当前连接数已经达到最大，只能等待，释放连接
				do {
					logger.info("***No Available Connection, Retrying***");
					try {
						wait(interval);
					} catch (InterruptedException e) {
						logger.info("等待释放连接出错");
					}
					conn = getConnection();
				} while (conn == null);
			}
		}
		return conn;
	}
	
	public void emptyPool() {
		for (int x = 0; x < conns.size(); x++) {
			try {
				conns.get(x).close();
				logger.info("Close---{}", conns.get(x));
			} catch (SQLException e) {
				logger.info("Close Exception:", e.getMessage());
			}
		}
		conns.clear();
		url = null;
		username = null;
		password = null;
		conns = null;
		logger.info("*** ConnectionPool ShutDown ***");
	}
}
